/**
 * BomberBoy, CPS240 Term Project - Team A
 * Andrew Osterink, David Bash, Scott Kidder
 * 
 * This class handles collision dispatches. This class also
 * has added functionality to register any function for an action.
 * 
 *  Something I might add later is some default collision code for a genaric entity.
 */
import java.awt.geom.*;
import java.lang.reflect.*;
import java.util.*;

public class Collider<T extends Entity> extends Dispatcher<Entity> implements DispatchAction<Entity> {
	
	protected DispatchAction<Entity> defaultCollide;
	protected T owner;
	public Collider(T owner)
	{
		this();
		this.owner = owner;
		
	}
	
	protected Collider()
	{
		setDefault(this);
	}
	
	/**
	 * Checks for collisions with other entitys.
	 * @return true if this object collided with another object
	 *  false if not
	 */
	public boolean checkCollisions()
	{
		List<Entity> entitys = owner.getManager().getEntitys(EntityManager.Filters.allWithin(owner.bounds));
		
		for(Entity entity : entitys)
			if(entity != owner)
			{
				owner.collider.fireEvent(owner, entity);
				entity.collider.fireEvent(owner, entity); //notify other party member
			}
		return entitys.size() > 1 ? true : false;
	}
	
	/**
	 * Registers any function to handle a collision dispatch
	 * for an object. example register(Block,"onPlayerHitBlock")
	 * @param type object type to create a special collision case for.
	 * @param methodName full name of method to call when that collision happens. (Case sensitive)
	 */
	public <U> void register(Class<U> type,String methodName)
	{
		DelegateDispatch<U> dispatch = new DelegateDispatch<U>((Object)owner,type,methodName);
		this.register(owner.getClass(), type,dispatch );
	}
	
	public boolean fireEvent(Object obj1,Object obj2)
	{
		boolean didFire = super.fireEvent(obj1, obj2);
		
		if(!didFire || obj1 instanceof Entity && obj2 instanceof Entity)
			defaultCollide.execute((Entity)obj1, (Entity)obj2);
		else if(!didFire)
			return false; //types arnt valid for default handler
		return true;
	}
	
	/**
	 * Set default collision handler to execute if no case type is found.
	 * Default handlers are forced to be of Type Entity or
	 * they will not fire. due to the fact we don't have type information on the handler
	 * set.
	 * @param defaultHandle default dispatch action class method
	 */
	public void setDefault(DispatchAction<Entity> defaultHandle)
	{
		this.defaultCollide = defaultHandle;
	}
	
	public void execute(Entity enity,Entity entity2)
	{
		//do nothing
	}

	class DelegateDispatch<T> implements DispatchAction<Entity>
	{

		Method method;
		Class owner;
		Class<T> type;
		public DelegateDispatch(Object owner,Class<T> type,String methodName) {
			
			try {
				method = owner.getClass().getMethod(methodName,type);
				this.owner = owner.getClass();
			} catch (Exception ex) {
				System.out.println("Collision handler has invalid method name for");

				/* Gave a null pointer exception
				System.out.println("Type " + this.owner.getName() + " with method name " + methodName);
				*/
				
				ex.printStackTrace();
				
			}
		}
		public void execute(Entity obj1, Entity obj2) {
			// TODO Auto-generated method stub
			if(method!= null)
			{
				T casted = type.cast(obj2);
				try {
					method.invoke(owner, casted);
				} catch (Exception ex) {
					ex.printStackTrace();
				} 
			}
		}
	}
}
